import * as THREE from 'three'
import * as Cesium from 'cesium'
import { CSS3DSprite, CSS3DRenderer } from 'three/examples/jsm/Addons.js'
import { EffectComposer, RenderPass, UnrealBloomPass, GlitchPass, ShaderPass } from 'three/examples/jsm/Addons.js'

const tianditutkArr = ['590447cdbdf97775b57588a69ebb903d', '75f1cc913e8d68f7bd4b6f6383f78ceb', '4c4c0f03b0bdb58811f657fec0c09eb5']
// ['590447cdbdf97775b57588a69ebb903d', '75f1cc913e8d68f7bd4b6f6383f78ceb', '4c4c0f03b0bdb58811f657fec0c09eb5']
const tianditutk = tianditutkArr[Math.floor(Math.random() * tianditutkArr.length)]
Cesium.Ion.defaultAccessToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJhZjE5ZTBhZC00YzE1LTQyMWYtOWM4MC00NmFlZjZhOGI3ZDciLCJpZCI6MTI4NTI2LCJpYXQiOjE2OTg4MzczOTN9.26EHXec7Ufty_B4e5_YWcB0t--zlquly7r66WVSS9wE'
// 控制Cesium的默认视角
Cesium.Camera.DEFAULT_VIEW_RECTANGLE = Cesium.Rectangle.fromDegrees(108.4, 21.3, 129.3, 48.6)
export default class TCesium {
  constructor(div, minWGS84, maxWGS84, centerWGS84) {
    this.div = document.getElementById(div)
    this.minWGS84 = minWGS84
    this.maxWGS84 = maxWGS84
    this.centerWGS84 = centerWGS84
    this.cesium = {
      viewer: null
    }
    this.three = {
      scene: null,
      camera: null,
      renderer: null,
      cssRenderer: null,
      composer: null,
    }
    this._3Dobjects = []
    this.init()
  }
  init() {
    this.initCesium()
    this.initThree()
    this.initObject3d()
    this.bindClick()
  }
  // 创建cesium 地球
  initCesium() {
    this.cesium.viewer = new Cesium.Viewer(this.div, {
      infoBox: false,
      selectionIndicator: false,
      homeButton: false,
      sceneModePicker: false,
      navigationHelpButton: false,
      navigationInstructionsInitiallyVisible: false,
      animation: false,
      timeline: false,
      fullscreenButton: false,
      allowTextureFilterAnisotropic: false,
      baseLayerPicker: false,
      contextOptions: {
        // 默认开启webgl2
        requestWebgl1: false,
        webgl: {
          alpha: false,
          antialias: true,
          preserveDrawingBuffer: true,
          failIfMajorPerformanceCaveat: false,
          depth: true,
          stencil: false,
          anialias: false
        },
      },
      targetFrameRate: 60,
      resolutionScale: 0.1,
      orderIndependentTranslucency: true,
      //加载底图
      baseLayer: new Cesium.ImageryLayer(new Cesium.WebMapTileServiceImageryProvider({
        url: "http://t0.tianditu.com/vec_w/wmts?service=wmts&request=GetTile&version=1.0.0&LAYER=vec&tileMatrixSet=w&TileMatrix={TileMatrix}&TileRow={TileRow}&TileCol={TileCol}&style=default&format=tiles&tk=" + tianditutk,
        layer: "tdtVecLayer",
        style: "default",
        format: "image/jpeg",
        tileMatrixSetID: "GoogleMapsCompatible",//使用谷歌的瓦片切片方式
        show: true
      })),
      geocoder: false,
      automaticallyTrackDataSourceClocks: false,
      dataSources: null,
      clock: null,
      terrainShadows: Cesium.ShadowMode.DISABLED,
      // 添加天空盒
      // skyBox: new Cesium.SkyBox({
      //   sources: {
      //     positiveX: 'park/posx.jpg',
      //     negativeX: 'park/negx.jpg',
      //     positiveY: 'park/posx.jpg',
      //     negativeY: 'park/negx.jpg',
      //     positiveZ: 'park/posy.jpg',
      //     negativeZ: 'park/negy.jpg'
      //   }
      // })
    })
    // 得到中心点
    const center = Cesium.Cartesian3.fromDegrees(
      this.centerWGS84 ? this.centerWGS84[0] : (this.minWGS84[0] + this.maxWGS84[0]) / 2,
      this.centerWGS84 ? this.centerWGS84[1] : ((this.minWGS84[1] + this.maxWGS84[1]) / 2) - 1,
      500000
    )
    // 开启深度测试，控制视角不转到地下
    this.cesium.viewer.scene.globe.depthTestAgainstTerrain = true
    // 相机移动到中心点并设置朝向
    // this.cesium.viewer.camera.flyTo({
    //   destination: center,
    //   orientation: {
    //     heading: Cesium.Math.toRadians(0),
    //     pitch: Cesium.Math.toRadians(-60),
    //     roll: Cesium.Math.toRadians(0)
    //   },
    //   duration: 3
    // })
  }
  // 创建three
  initThree() {
    this.three.scene = new THREE.Scene()
    this.three.renderer = new THREE.WebGLRenderer({
      antialias: true,    // 开启优化抗锯齿
      preserveDrawingBuffer: true,  // 开启保留缓存
      // 设置深度缓冲区问题，优化深度冲突问题
      logarithmicDepthBuffer: true  // 当两个面间距小的时候，可以让threejs更容易区分两个面，谁在前，谁在后
    })
    // 新建相机
    this.three.camera = new THREE.PerspectiveCamera(
      45,   // 可视角度
      // 宽高比
      document.documentElement.clientWidth / document.documentElement.clientHeight,
      1,
      10 * 1000 * 1000
    )
    // 新建控制器
    // 新建渲染器
    this.three.renderer.setSize(window.innerWidth, window.innerHeight)
    this.three.renderer.setPixelRatio(window.devicePixelRatio)
    // 设置背景色为黑色和透明色
    this.three.renderer.setClearColor(0x000000, 0)
    this.three.renderer.domElement.style.position = 'absolute'
    this.three.renderer.domElement.style.top = '0'
    this.three.renderer.domElement.style.left = '0'
    this.three.renderer.domElement.style.pointerEvents = 'none'
    this.cesium.viewer.cesiumWidget.canvas.parentElement.appendChild(this.three.renderer.domElement)
    this.three.cssRenderer = new CSS3DRenderer()
    this.three.cssRenderer.setSize(window.innerWidth, window.innerHeight)
    this.three.cssRenderer.domElement.style.position = 'absolute'
    this.three.cssRenderer.domElement.style.top = 0
    this.three.cssRenderer.domElement.style.pointerEvents = 'none'
    this.cesium.viewer.cesiumWidget.canvas.parentElement.appendChild(this.three.cssRenderer.domElement)
    this.three.composer = new EffectComposer(this.three.renderer)
    this.three.composer.addPass(new RenderPass(this.three.scene, this.three.camera))
    // this.three.composer.addPass(new UnrealBloomPass(new THREE.Vector2(window.innerWidth, window.innerHeight), 1.5, 0.4, 0.85))
    // this.three.composer.addPass(new GlitchPass())   // 添加恐怖后处理镜头
    // 添加法向纹理合成的特色渲染镜头
    const shaderPass = new ShaderPass({
      uniforms: {
        tDiffuse: { value: null },
        uNormalMap: { value: null }
      },
      vertexShader: `
      varying vec2 vUv;
      void main() {
        vUv = uv;
        gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
      }
      `,
      fragmentShader: `
      varying vec2 vUv;
      uniform sampler2D tDiffuse;
      uniform sampler2D uNormalMap;
      void main() {
        vec4 color = texture2D(tDiffuse, vUv);
        vec4 normalColor = texture2D(uNormalMap, vUv);
        // 设置光线的角度
        vec3 lightDir = normalize(vec3(-5.0, 5.0, 1.0));
        // 计算光线明亮程度
        float lightIntensity = clamp(dot(normalColor.xyz, lightDir), 0.0, 1.0);
        // 颜色叠加光线明亮程度
        color.xyz += lightIntensity;
        gl_FragColor = color;
      }
      `
    })
    const normalTexture = new THREE.TextureLoader().load('interfaceNormalMap.png')
    shaderPass.material.uniforms.uNormalMap.value = normalTexture
    // this.three.composer.addPass(shaderPass)
  }
  // 创建three 3d物体
  initObject3d() {
    let geometry = new THREE.PlaneGeometry(80000, 80000)
    // let material = new THREE.MeshPhongMaterial()
    let meshGroup = new THREE.Group()

    const moonDiv = document.createElement('div')
    moonDiv.className = 'label'
    moonDiv.textContent = 'Moon'
    moonDiv.style.backgroundColor = 'yellow'

    // CSS3DSprite创建的对象会跟随摄像头的变化而变化
    const moonLabel = new CSS3DSprite(moonDiv)
    moonLabel.scale.set(100, 100, 100)
    moonLabel.position.set(0, 10000, 0)
    meshGroup.add(moonLabel)
    const axesHelper = new THREE.AxesHelper(1000000)
    meshGroup.add(axesHelper)
    const light = new THREE.AmbientLight(0xffffff, 0.2)
    const dLight = new THREE.DirectionalLight('red', 1)
    dLight.position.set(-10000, -10000, 10000)
    dLight.target.position.set(0, 0, 0)
    meshGroup.add(light)
    meshGroup.add(dLight)
    // 自定义顶点着色器
    const vertexShader = `
    varying vec2 vUv;
    void main() {
      vUv = uv;
      gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
    }
    `
    // 自定义片元着色器
    const fragmentShader2 = `
    varying vec2 vUv;
    uniform float uTime;
    void main() {
      float dist = distance(vUv, vec2(0.5));
      float radius = 0.5 * (sin(uTime) * 0.5 + 0.5);
      float alpha = 1.0 - step(radius, dist);
      gl_FragColor = vec4(vec3(0.0), alpha);
    }
    `
    // 定义材质
    const material = new THREE.ShaderMaterial({
      uniforms: {
        uTime: { value: 0 }
      },
      vertexShader,
      fragmentShader: fragmentShader2,
    })
    let mesh = new THREE.Mesh(geometry, material)
    mesh.castShadow = true
    mesh.receiveShadow = true
    // 放大物体
    // mesh.scale.set(20000, 20000, 2000) // 放大
    meshGroup.add(mesh)
    let time = 0;
    (function animate() {
      time += 0.05
      material.uniforms.uTime.value = time
      requestAnimationFrame(animate)
    })();
    // 添加至场景
    this.three.scene.add(meshGroup)
    this._3Dobjects.push({
      threeMesh: meshGroup,
      centerWGS84: [116.4, 39.9]
    })
  }
  // 渲染Cesium
  renderCesium() {
    this.cesium.viewer.render()
  }
  // 渲染Three
  renderThree() {
    this.three.camera.fov = Cesium.Math.toDegrees(this.cesium.viewer.camera.frustum.fovy)
    //更新相机矩阵
    this.three.camera.updateProjectionMatrix()
    // 声明一个将cesium框架的cartesian3转换为three.js的vector3（笛卡尔坐标转换为三维向量）
    let cartToVec = function (cart) {
      return new THREE.Vector3(cart.x, cart.y, cart.z)
    }
    // 将3D的物体通过经纬度转换成对应的位置
    this._3Dobjects.forEach((item, index) => {
      // 通过经纬度获取中心点的位置
      let center = Cesium.Cartesian3.fromDegrees(
        item.centerWGS84 ? item.centerWGS84[0] : (item.minWGS84[0] + item.maxWGS84[0]) / 2,
        item.centerWGS84 ? item.centerWGS84[1] : (item.minWGS84[1] + item.maxWGS84[1]) / 2
      )
      //计算朝向（切面方向-切线向量）
      //中心高度点
      let centerHeight = Cesium.Cartesian3.fromDegrees(
        item.centerWGS84 ? item.centerWGS84[0] : (item.minWGS84[0] + item.maxWGS84[0]) / 2,
        item.centerWGS84 ? item.centerWGS84[1] : (item.minWGS84[1] + item.maxWGS84[1]) / 2,
        1
      )
      item.threeMesh.position.copy(center)
      //设置查看方向
      item.threeMesh.lookAt(centerHeight.x, centerHeight.y, centerHeight.z)
      //设置上方向
      item.threeMesh.up.set(0, 0, 1)
    })
    //设置摄像机矩阵
    // 设置相机跟cesium保持一致
    this.three.camera.matrixAutoUpdate = false //自动更新
    //复制cesium相机矩阵
    let cvm = this.cesium.viewer.camera.viewMatrix
    let civm = this.cesium.viewer.camera.inverseViewMatrix
    // three相机默认朝向0,0,0
    this.three.camera.lookAt(0, 0, 0)
    // 设置threejs相机矩阵，使用set方法，通过设置行主序矩阵，内部转换为列主序矩阵
    this.three.camera.matrixWorld.set(
      civm[0], civm[4], civm[8], civm[12],
      civm[1], civm[5], civm[9], civm[13],
      civm[2], civm[6], civm[10], civm[14],
      civm[3], civm[7], civm[11], civm[15]
    )

    this.three.camera.matrixWorldInverse.set(
      cvm[0], cvm[4], cvm[8], cvm[12],
      cvm[1], cvm[5], cvm[9], cvm[13],
      cvm[2], cvm[6], cvm[10], cvm[14],
      cvm[3], cvm[7], cvm[11], cvm[15]
    )
    //设置宽高比例
    let width = this.div.clientWidth
    let height = this.div.clientHeight
    this.three.camera.aspect = width / height
    this.three.camera.updateProjectionMatrix()
    //设置尺寸大小
    this.three.renderer.setSize(width, height)
    this.three.renderer.clear()
    this.three.composer.render(this.three.scene, this.three.camera)
    this.three.cssRenderer.render(this.three.scene, this.three.camera)
  }
  // 绑定点击事件
  bindClick() {
    this.cesium.viewer.scene.globe.depthTestAgainstTerrain = true
    let handler = new Cesium.ScreenSpaceEventHandler(this.cesium.viewer.scene.canvas)
    handler.setInputAction((pick) => {
      // 获取cesium中点击的物体
      const pickObj = this.cesium.viewer.scene.pick(pick.position)
      console.log(pickObj)
      // 获取three中对应的物体
      const threeObj = this.getThreeClick(pick.position)
      console.log(threeObj)
    }, Cesium.ScreenSpaceEventType.LEFT_CLICK)
  }
  // 获取three中点击的坐标和物体
  getThreeClick(position) {
    const raycaster = new THREE.Raycaster()
    const pointer = new THREE.Vector2()
    const cesiumP = this.cesium.viewer.scene.canvas.getBoundingClientRect()
    const { width, height, top, left } = this.three.renderer.domElement.getBoundingClientRect()
    const offsetx = this.three.renderer.domElement.offsetLeft
    const offsety = this.three.renderer.domElement.offsetTop
    const x = position.x + cesiumP.x - left + offsetx
    const y = position.y + cesiumP.y - top + offsety
    pointer.x = (x / width) * 2 - 1
    pointer.y = - (y / height) * 2 + 1
    const clickObjects = this.three.scene.children
    // 通过摄像机和鼠标位置更新射线
    raycaster.setFromCamera(pointer, this.three.camera)

    // 计算物体和射线的焦点
    const intersects = raycaster.intersectObjects(clickObjects)
    return intersects[0]
  }
  addMesh(mesh, option) {
    // 放大物体
    mesh.scale.set(2000, 2000, 2000) // 放大
    this.three.scene.add(mesh)
    this._3Dobjects.push({
      threeMesh: mesh,
      minWGS84: option.minWGS84,
      maxWGS84: option.maxWGS84,
      centerWGS84: option.centerWGS84
    })
  }
}
